package com.cencat.common.utils;

import lombok.experimental.UtilityClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.CollectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Bean工具类
 * 提供对象属性复制、转换等功能
 * 
 * @author cencat
 * @since 2024-01-01
 */
@UtilityClass

public class BeanUtils {

    private static final Logger log = LoggerFactory.getLogger(BeanUtils.class);

    /**
     * BeanCopier缓存
     */
    private static final Map<String, BeanCopier> BEAN_COPIER_CACHE = new ConcurrentHashMap<>();

    /**
     * 复制对象属性
     * 
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        if (source == null || target == null) {
            return;
        }
        
        try {
            org.springframework.beans.BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
            log.error("复制对象属性失败: source={}, target={}, error={}", 
                    source.getClass().getSimpleName(), target.getClass().getSimpleName(), e.getMessage());
        }
    }

    /**
     * 复制对象属性（忽略null值）
     * 
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyPropertiesIgnoreNull(Object source, Object target) {
        if (source == null || target == null) {
            return;
        }
        
        try {
            org.springframework.beans.BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
        } catch (Exception e) {
            log.error("复制对象属性失败: source={}, target={}, error={}", 
                    source.getClass().getSimpleName(), target.getClass().getSimpleName(), e.getMessage());
        }
    }

    /**
     * 复制对象属性（忽略指定属性）
     * 
     * @param source           源对象
     * @param target           目标对象
     * @param ignoreProperties 忽略的属性名
     */
    public static void copyPropertiesIgnore(Object source, Object target, String... ignoreProperties) {
        if (source == null || target == null) {
            return;
        }
        
        try {
            org.springframework.beans.BeanUtils.copyProperties(source, target, ignoreProperties);
        } catch (Exception e) {
            log.error("复制对象属性失败: source={}, target={}, error={}", 
                    source.getClass().getSimpleName(), target.getClass().getSimpleName(), e.getMessage());
        }
    }

    /**
     * 使用CGLib复制对象属性（性能更好）
     * 
     * @param source      源对象
     * @param targetClass 目标类
     * @param <T>         目标类型
     * @return 目标对象
     */
    public static <T> T copyPropertiesFast(Object source, Class<T> targetClass) {
        if (source == null) {
            return null;
        }
        
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            copyPropertiesFast(source, target);
            return target;
        } catch (Exception e) {
            log.error("快速复制对象属性失败: source={}, target={}, error={}", 
                    source.getClass().getSimpleName(), targetClass.getSimpleName(), e.getMessage());
            return null;
        }
    }

    /**
     * 使用CGLib复制对象属性（性能更好）
     * 
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyPropertiesFast(Object source, Object target) {
        if (source == null || target == null) {
            return;
        }
        
        try {
            String key = generateKey(source.getClass(), target.getClass());
            BeanCopier copier = BEAN_COPIER_CACHE.get(key);
            if (copier == null) {
                copier = BeanCopier.create(source.getClass(), target.getClass(), false);
                BEAN_COPIER_CACHE.put(key, copier);
            }
            copier.copy(source, target, null);
        } catch (Exception e) {
            log.error("快速复制对象属性失败: source={}, target={}, error={}", 
                    source.getClass().getSimpleName(), target.getClass().getSimpleName(), e.getMessage());
        }
    }

    /**
     * 复制列表对象
     * 
     * @param sourceList  源列表
     * @param targetClass 目标类
     * @param <S>         源类型
     * @param <T>         目标类型
     * @return 目标列表
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Class<T> targetClass) {
        if (CollectionUtils.isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        
        List<T> targetList = new ArrayList<>(sourceList.size());
        for (S source : sourceList) {
            T target = copyPropertiesFast(source, targetClass);
            if (target != null) {
                targetList.add(target);
            }
        }
        return targetList;
    }

    /**
     * 将对象转换为Map
     * 
     * @param obj 对象
     * @return Map
     */
    public static Map<String, Object> beanToMap(Object obj) {
        if (obj == null) {
            return new HashMap<>();
        }
        
        Map<String, Object> map = new HashMap<>();
        try {
            BeanWrapper beanWrapper = new BeanWrapperImpl(obj);
            PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
            
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (!"class".equals(key)) {
                    Object value = beanWrapper.getPropertyValue(key);
                    map.put(key, value);
                }
            }
        } catch (Exception e) {
            log.error("对象转Map失败: obj={}, error={}", obj.getClass().getSimpleName(), e.getMessage());
        }
        
        return map;
    }

    /**
     * 将Map转换为对象
     * 
     * @param map         Map
     * @param targetClass 目标类
     * @param <T>         目标类型
     * @return 对象
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> targetClass) {
        if (CollectionUtils.isEmpty(map)) {
            return null;
        }
        
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            BeanWrapper beanWrapper = new BeanWrapperImpl(target);
            
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                
                if (beanWrapper.isWritableProperty(key) && value != null) {
                    beanWrapper.setPropertyValue(key, value);
                }
            }
            
            return target;
        } catch (Exception e) {
            log.error("Map转对象失败: target={}, error={}", targetClass.getSimpleName(), e.getMessage());
            return null;
        }
    }

    /**
     * 获取对象的所有字段名
     * 
     * @param clazz 类
     * @return 字段名列表
     */
    public static List<String> getFieldNames(Class<?> clazz) {
        List<String> fieldNames = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();
        
        for (Field field : fields) {
            fieldNames.add(field.getName());
        }
        
        return fieldNames;
    }

    /**
     * 获取对象的字段值
     * 
     * @param obj       对象
     * @param fieldName 字段名
     * @return 字段值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        if (obj == null || CencatStringUtils.isBlank(fieldName)) {
            return null;
        }
        
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            log.error("获取字段值失败: obj={}, fieldName={}, error={}", 
                    obj.getClass().getSimpleName(), fieldName, e.getMessage());
            return null;
        }
    }

    /**
     * 设置对象的字段值
     * 
     * @param obj       对象
     * @param fieldName 字段名
     * @param value     字段值
     */
    public static void setFieldValue(Object obj, String fieldName, Object value) {
        if (obj == null || CencatStringUtils.isBlank(fieldName)) {
            return;
        }
        
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        } catch (Exception e) {
            log.error("设置字段值失败: obj={}, fieldName={}, value={}, error={}", 
                    obj.getClass().getSimpleName(), fieldName, value, e.getMessage());
        }
    }

    /**
     * 调用对象的方法
     * 
     * @param obj        对象
     * @param methodName 方法名
     * @param args       参数
     * @return 方法返回值
     */
    public static Object invokeMethod(Object obj, String methodName, Object... args) {
        if (obj == null || CencatStringUtils.isBlank(methodName)) {
            return null;
        }
        
        try {
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i].getClass();
            }
            
            Method method = obj.getClass().getDeclaredMethod(methodName, paramTypes);
            method.setAccessible(true);
            return method.invoke(obj, args);
        } catch (Exception e) {
            log.error("调用方法失败: obj={}, methodName={}, error={}", 
                    obj.getClass().getSimpleName(), methodName, e.getMessage());
            return null;
        }
    }

    /**
     * 判断对象是否为空（所有字段都为null）
     * 
     * @param obj 对象
     * @return 是否为空
     */
    public static boolean isEmpty(Object obj) {
        if (obj == null) {
            return true;
        }
        
        try {
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                Object value = field.get(obj);
                if (value != null) {
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            log.error("判断对象是否为空失败: obj={}, error={}", obj.getClass().getSimpleName(), e.getMessage());
            return false;
        }
    }

    /**
     * 获取对象中为null的属性名
     * 
     * @param source 源对象
     * @return null属性名数组
     */
    private static String[] getNullPropertyNames(Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        
        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        
        return emptyNames.toArray(new String[0]);
    }

    /**
     * 生成BeanCopier缓存key
     * 
     * @param sourceClass 源类
     * @param targetClass 目标类
     * @return 缓存key
     */
    private static String generateKey(Class<?> sourceClass, Class<?> targetClass) {
        return sourceClass.getName() + "_" + targetClass.getName();
    }
}